package database;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Scanner;

import type.Movie;
import type.Rating;
import type.Sex;
import type.User;

public class MovieLensDatabase {

	String _databaseFolder;
	HashMap<Integer,String> _genres;
	ArrayList<String> _occupations;
	HashMap<Integer,User> _users;
	HashMap<Integer,Movie> _movies;
	ArrayList<ArrayList<Rating>> _baseRatings;
	ArrayList<ArrayList<Rating>> _testRatings;
	
	@SuppressWarnings("unchecked")
	public ArrayList<Rating> selectRatingSet(int index){
		// clear previous rating
		for (User user : _users.values()) {
			user.clearRating();
		}
		for (Movie movie : _movies.values()) {
			movie.clearRating();
		}
		
		// load learning data
		for (Rating rating: _baseRatings.get(index)) {
			rating.getUser().addRating(rating);
			rating.getMovie().addRating(rating);
		}
		
		for(User user: _users.values())
		{
			user.computeMeanRating();
		}
		
		for(Movie movie: _movies.values())
		{
			movie.computeMeanRating();
		}
		//return the test rating
		return (ArrayList<Rating>) _testRatings.get(index).clone();
	}
	
	public MovieLensDatabase(String databaseFolder){
		_databaseFolder = databaseFolder;
		_genres = new HashMap<Integer, String>();
		_occupations = new ArrayList<String>();
		_users = new HashMap<Integer,User>() ;
		_movies = new HashMap<Integer,Movie>();
		_baseRatings = new ArrayList<ArrayList<Rating>>();
		_testRatings = new ArrayList<ArrayList<Rating>>();
		
		loadGenre();
		loadOccupation();
		loadUser();
		loadMovie();
		loadRating();
	}
	
	@Override
	public String toString() {
		String str = "databaseFolder : " + _databaseFolder + "\n";
		
		str += "---Genre---\n";
		for (String genre : _genres.values()) {
			str += 	genre + "\n";	
		}
		
		str += "---Occupation---\n";
		for (String occupation : _occupations) {
			str += 	occupation + "\n";	
		}
		
		str += "---User---\n";
		for (User user : _users.values()) {
			str += 	user.toString() + "\n";	
		}
		
		str += "---Movie---\n";
		for (Movie movie : _movies.values()) {
			str += 	movie.toString() + "\n";	
		}
		
		/* WAY TO LONG (CONCATENATIONS)
		str += "---Base Rating---\n";
		int index = 1;
		for (ArrayList<Rating> baseRating : _baseRatings) {
			str += "+++Base"+(index++)+"+++\n";
			for (Rating rating : baseRating){
				str += 	rating.toString() + "\n";	
			}
		}
		
		str += "---Test Rating---\n";
		index = 1;
		for (ArrayList<Rating> baseRating : _testRatings) {
			str += "+++Test"+(index++)+"+++\n";
			for (Rating rating : baseRating){
				str += 	rating.toString() + "\n";	
			}
		}
		*/
		return str;
	}
	
	private void loadGenre(){
		ArrayList<String> genreFile = readFile("u.genre");
		for (String genre : genreFile) {
			String [] splitGenre = genre.split("\\|");
			if (splitGenre.length >= 2)
				_genres.put(new Integer(splitGenre[1]), splitGenre[0]) ;
		}
		Movie.setGenreTable(_genres);
	}
	
	private void loadOccupation(){
		ArrayList<String> occupationFile = readFile("u.occupation");
		for (String occupation : occupationFile) {
			if (occupation.length() >= 0)
				_occupations.add(occupation);
		}
	}
	
	private void loadUser(){
		ArrayList<String> userFile = readFile("u.user");
		for (String user : userFile) {
			String [] splitUser = user.split("\\|");
			if (splitUser.length >= 5){
				int userID = new Integer(splitUser[0]).intValue();
				int age = new Integer(splitUser[1]).intValue();
				Sex sex =  splitUser[2].compareToIgnoreCase("M") == 0 ? Sex.Male : Sex.Female;
				String occupation = splitUser[3];
				String zipCode = splitUser[4];
				_users.put(userID, new User(userID, age, sex, occupation, zipCode)) ;
			}
		}
	}

	private void loadMovie(){
		ArrayList<String> movieFile = readFile("u.item");
		for (String movie : movieFile) {
			String [] splitMovie = movie.split("\\|");
			if (splitMovie.length >= 24){

				int movieID = new Integer(splitMovie[0]).intValue();
				String title = splitMovie[1];
				Date releaseDate = new Date();
				try {
					SimpleDateFormat dateFormater = new SimpleDateFormat("dd-MMM-yyyy", Locale.CANADA);
					String strDate = splitMovie[2];
					releaseDate = dateFormater.parse(strDate);
				} catch (ParseException e) {
					System.err.println("movie data corrupted movieID: " + movieID);
				}
				String imdbURL = splitMovie[4];
				ArrayList<Integer> genres = new ArrayList<Integer>();
				for(int i= 5; i<24; ++i){
					genres.add(new Integer(splitMovie[i]));
				}
				_movies.put(movieID, new Movie(movieID, title, releaseDate, imdbURL, genres ) ) ;

			}
		}
	}
	
	private void loadRating(){
		for (int index=1; index<=5; ++index)
		{
			ArrayList<String> baseRatingFile = readFile("u"+index+".base");
			ArrayList<String> testRatingFile = readFile("u"+index+".test");
			
			_baseRatings.add( new ArrayList<Rating>());
			_testRatings.add( new ArrayList<Rating>());
			
			for (String baseRating : baseRatingFile) {
				String [] splitBaseRating = baseRating.split("\\t");
				if (splitBaseRating.length >= 4){

					int userId = new Integer(splitBaseRating[0]).intValue();
					int movieID = new Integer(splitBaseRating[1]).intValue();
					int ratingValue = new Integer(splitBaseRating[2]).intValue();
					Date date = new Date(Long.parseLong(splitBaseRating[3]) * 1000);
					User user = _users.get(userId);
					Movie movie = _movies.get(movieID);
					Rating rating = new Rating(user, movie, ratingValue, date);
					_baseRatings.get(index-1).add(rating);
				}
			}
			
			for (String testRating : testRatingFile) {
				String [] splitBaseRating = testRating.split("\\t");
				if (splitBaseRating.length >= 4){

					int userId = new Integer(splitBaseRating[0]).intValue();
					int movieID = new Integer(splitBaseRating[1]).intValue();
					int ratingValue = new Integer(splitBaseRating[2]).intValue();
					Date date = new Date(Long.parseLong(splitBaseRating[3]) * 1000);
					User user = _users.get(userId);
					Movie movie = _movies.get(movieID);
					Rating rating = new Rating(user, movie, ratingValue, date);
					_testRatings.get(index-1).add(rating);
				}
			}
		}
		
	}
	
	private ArrayList<String> readFile(String fileName){
		ArrayList<String> file = new ArrayList<String>();
		Path path = Paths.get(_databaseFolder + "\\"+ fileName);
		try {
			Scanner scanner =  new Scanner(path, StandardCharsets.UTF_8.name());
			while (scanner.hasNextLine()){
				file.add(scanner.nextLine());
			} 
			scanner.close();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		return file;
	}
	
	public static void main(String[] args) {
		MovieLensDatabase db = new MovieLensDatabase("..\\..\\database\\ml-100k");
		System.out.println(db.toString());
	}
}
